#include <iostream>
#include <assert.h>
#include <fstream>
#include "GLShader.h"
#include "GL/glew.h"

void ReportError(const char* buf)
{
  std::cout << buf << "\n";
}

GLShader::GLShader()
{
  m_program = 0;
}

std::string GLShader::ReadFile(const std::string& FileName)
{
	std::string result;

	std::ifstream TempFile;

	TempFile.open(FileName.c_str());

	if(TempFile)
	{
		while(!TempFile.eof())
		{
			std::string str;
			std::getline(TempFile, str);
			result += str + "\n";
		}
	}
	else
	{
		std::cout << "File " << FileName << " Doesn't exist";
	}
	return result;
}

bool GLShader::Load(const std::string& VertexFilename, const std::string& FragFilename)
{
	std::string VS = ReadFile(VertexFilename);
	std::string FS = ReadFile(FragFilename);

	return Create(VS, FS);
}

bool GLShader::Create(const std::string& vertexSource, const std::string& fragmentSource)
{
  GLuint vertSh = glCreateShader(GL_VERTEX_SHADER);
  GLuint fragSh = glCreateShader(GL_FRAGMENT_SHADER);

  const GLint vlength = vertexSource.size();
  const GLint flength = fragmentSource.size();
  const char* vStr = vertexSource.c_str();
  const char* fStr = fragmentSource.c_str();
  glShaderSource(vertSh, 1, &vStr, &vlength);
  glShaderSource(fragSh, 1, &fStr, &flength);

  GLint compiled = 0;
  static const int ERROR_BUF_SIZE = 2000;
  GLcharARB buf[ERROR_BUF_SIZE]; // error string buffer

  glCompileShader(vertSh);

  glGetShaderiv(vertSh, GL_COMPILE_STATUS, &compiled);
  if (!compiled)
  {
    glGetShaderInfoLog(vertSh, ERROR_BUF_SIZE, 0, buf);
    ReportError(buf);
    return false;
  }

  glCompileShader(fragSh);
  
  glGetShaderiv(fragSh, GL_COMPILE_STATUS, &compiled);
  if (!compiled)
  {
    glGetShaderInfoLog(fragSh, ERROR_BUF_SIZE, 0, buf);
    ReportError(buf);
    return false;
  }

  m_program = glCreateProgram();
  glAttachShader(m_program, vertSh);
  glAttachShader(m_program, fragSh);
  glLinkProgram(m_program);
  GLint linked;
  glGetProgramiv(m_program, GL_OBJECT_LINK_STATUS_ARB, &linked);
  if (!linked)
  {
    glGetProgramInfoLog(m_program, ERROR_BUF_SIZE, 0, buf);
    ReportError(buf);
    return false;
  }

  return true;
}

void GLShader::Set(const std::string& name, float f)
{
	GLint lock = glGetUniformLocation(m_program, name.c_str());

	assert(lock != -1);

	glUniform1f(lock, f);
}

void GLShader::UseThisShader()
{
  glUseProgram(m_program);
}
